package com.jubaozan.framework.sentinel.springmvc.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.redis.RedisDataSource;
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.jubaozan.framework.sentinel.config.ApplicationRedisValueConfig;
import com.jubaozan.framework.sentinel.constant.RuleKeyConstant;
import com.jubaozan.framework.sentinel.springmvc.listener.DegradePropertyListener;
import com.jubaozan.framework.sentinel.springmvc.listener.FlowPropertyListener;
import com.jubaozan.framework.sentinel.springmvc.listener.ParamPropertyListener;
import com.jubaozan.framework.sentinel.springmvc.listener.SystemPropertyListener;
import java.util.List;
import javax.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@RequiredArgsConstructor
@Configuration
public class SentinelConfig {
    private String channel = "sentinel.rules.flow.channel";

    @Autowired
    private ApplicationRedisValueConfig applicationRedisValueConfig;
    @Autowired
    FlowPropertyListener flowPropertyListener;
    @Autowired
    SystemPropertyListener systemPropertyListener;
    @Autowired
    ParamPropertyListener paramPropertyListener;

    @Autowired
    DegradePropertyListener degradePropertyListener;

    @Autowired
    RuleKeyConstant ruleKeyConstant;

    RedisConnectionConfig config;
    @PostConstruct
    public void init() {
         config = RedisConnectionConfig.builder()
            .withHost(applicationRedisValueConfig.getHost())
            .withPort(applicationRedisValueConfig.getPort())
            .withPassword(applicationRedisValueConfig.getPassword())
            .build();
    }


    @DependsOn(value = {"flowPropertyListener","instanceIdGenerator"})
    @Bean
    public ReadableDataSource<String, List<FlowRule>> flowRulePropertyListener() {
        Converter<String, List<FlowRule>> ruleConfigParser = source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});
        ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<List<FlowRule>>(config, ruleKeyConstant.getFlowRuleKey(), channel, ruleConfigParser);
        redisDataSource.getProperty().addListener(flowPropertyListener);
        FlowRuleManager.register2Property(redisDataSource.getProperty());
        return redisDataSource;
    }


    @DependsOn(value = {"systemPropertyListener","instanceIdGenerator"})
    @Bean
    public ReadableDataSource<String, List<SystemRule>> rulePropertyListener() {
        Converter<String, List<SystemRule>> ruleConfigParser = source -> JSON.parseObject(source, new TypeReference<List<SystemRule>>() {});
        ReadableDataSource<String, List<SystemRule>> redisDataSource = new RedisDataSource<List<SystemRule>>(config, ruleKeyConstant.getSystemRuleKey(), channel, ruleConfigParser);
        redisDataSource.getProperty().addListener(systemPropertyListener);
        SystemRuleManager.register2Property(redisDataSource.getProperty());
        return redisDataSource;
    }


    @DependsOn(value = {"paramPropertyListener","instanceIdGenerator"})
    @Bean
    public ReadableDataSource<String, List<ParamFlowRule>> paramFlowRulePropertyListener() {
        Converter<String, List<ParamFlowRule>> ruleConfigParser = source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {});
        ReadableDataSource<String, List<ParamFlowRule>> redisDataSource = new RedisDataSource<List<ParamFlowRule>>(config,
            ruleKeyConstant.getParamFlowRuleKey(), channel, ruleConfigParser);
        redisDataSource.getProperty().addListener(paramPropertyListener);
        ParamFlowRuleManager.register2Property(redisDataSource.getProperty());
        return redisDataSource;
    }


    @Bean
    public ReadableDataSource<String, List<DegradeRule>> redisDegradeDatasource() {
        Converter<String, List<DegradeRule>> ruleConfigParser = source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {});
        ReadableDataSource<String, List<DegradeRule>> redisDataSource = new RedisDataSource<List<DegradeRule>>(config,
            ruleKeyConstant.getDegradeRuleKey(), channel, ruleConfigParser);
        redisDataSource.getProperty().addListener(degradePropertyListener);
        DegradeRuleManager.register2Property(redisDataSource.getProperty());
        return redisDataSource;
    }



    @Bean
    public BlockExceptionHandler blockExceptionHandler() {
        return (request, response, ex) -> {
            String msg = "未知异常";
            int status = 1429;

            if (ex instanceof FlowException) {
                msg = "请求被限流";
            } else if (ex instanceof DegradeException) {
                msg = "服务已熔断";
                status = 1503;
            } else if (ex instanceof AuthorityException) {
                msg = "没有权限访问";
                status = 1403;
            }
            else if (ex instanceof ParamFlowException) {
                msg = "热点参数限流";
                status = 1404;
            }else if (ex instanceof SystemBlockException) {
                msg = "系统规则限流";
                status = 1405;
            }

            response.setContentType("application/json;charset=utf-8");
            response.setStatus(status);
            response.getWriter().println("{\"code\":" + status + ",\"message\":\"" + msg + "\"}");
        };
    }
}    